/*
 * This file is part of ELCube.
 *
 * ELCube is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * ELCube is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with ELCube.  If not, see <https://www.gnu.org/licenses/>.
 */
package cn.nkpro.elcube.components.defaults.services

import cn.nkpro.elcube.annotation.NkNote
import cn.nkpro.elcube.co.easy.EasySingle
import cn.nkpro.elcube.co.spel.NkSpELManager
import cn.nkpro.elcube.docengine.NkDocCycle
import cn.nkpro.elcube.docengine.NkDocEngine
import cn.nkpro.elcube.docengine.interceptor.abstracts.NkAbstractDocCycleInterceptor
import cn.nkpro.elcube.docengine.model.DocHV
import cn.nkpro.elcube.docengine.model.event.AbstractDocCycleEvent
import com.alibaba.fastjson.JSON
import org.apache.commons.lang.StringUtils
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import org.springframework.util.Assert

/**
 * 创建后修改前续单据的生命周期函数续单据
 */
@NkNote("修改前续单据的生命周期函数")
@SuppressWarnings("unused")
@Component("NkDocStateChangedAdjustPrevHandler")
class NkDocStateChangedAdjustPrevHandler
        extends NkAbstractDocCycleInterceptor {

    @Autowired
    private NkDocEngine docEngine
    @Autowired
    private NkSpELManager spELManager

    @Override
    void apply(DocHV doc, AbstractDocCycleEvent event) {

        // 限定生命周期事件
        if(event.getCycle() != NkDocCycle.stateChanged) {
            return
        }

        // 如果参数为空不处理
        if(StringUtils.isBlank(event.params)){
            return
        }

        Param param = JSON.parseObject(spELManager.convert(event.params, doc), Param.class)

        // 单据类型不能为null
        Assert.hasLength(param.targetDocType,"模型不能为null")

        // 单据状态是否满足
        if(param.docState != doc.docState){
            return
        }

        // 其他条件为null 或 为false
        if(!(param.condition==null||param.condition)){
            return
        }

        if(doc.preDocId){
            DocHV prev = doc
            while(true){
                prev = docEngine.detail(prev.preDocId)
                if(!prev || prev.docType==param.targetDocType){
                    break
                }
            }
            if(prev){
                docEngine.doUpdate(prev.docId, param.optSource, { target ->

                    if(param.targetDocData){
                        param.targetDocData.forEach({k,  v ->
                            if(v instanceof Map){
                                EasySingle single = target.fetch(k)
                                v.forEach({kk,vv->
                                    single.set(kk as String, vv)
                                })
                            }else{
                                target.getData().put(k, v)
                            }
                        })
                    }

                    if(param.calc || param.calculate){
                        docEngine.calculate(target, param.calcFromCard, param.calcOptions)
                    }

                    if(param.targetDocState)
                        target.setDocState(param.targetDocState)

                    if(param.targetDocName){
                        target.setDocName(param.targetDocName)
                    }
                })
            }
        }
    }


    static class Param{
        // 触发条件，默认true
        Boolean condition
        // 触发条件，当前单据状态
        String docState

        // 需要创建的单据类型
        String targetDocType
        // 需要创建的单据类型
        String targetDocState
        // 目标单据名称
        String targetDocName
        // 目标单据数据
        Map<String,Object> targetDocData

        // 计算单据
        Boolean calc
        Boolean calculate
        String calcFromCard
        Object calcOptions
        // 操作描述，可选
        String optSource
    }
}